#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>

#define BUFF_SIZE 5
char buffer[BUFF_SIZE];
int in,out = 0;
#define PRODUCT_COUNT 10
int semid = 0;

//封装P操作函数
void sem_wait(int semid, int index){
    struct sembuf sops;
    sops.sem_num = index;
    sops.sem_op = -1;
    sops.sem_flg = 0;
    if(semop(semid, &sops, 1) == -1){
        perror("semop");
        exit(EXIT_FAILURE);
    }
}

/**
 * @brief 向信号量集中指定信号量增加一
 *
 * 使用semop函数向信号量集中指定的信号量增加一。如果操作失败，则输出错误信息并退出程序。
 *
 * @param semid 信号量集的标识符
 * @param index 要增加的信号量在信号量集中的索引
 */
void sem_post(int semid, int index){
    struct sembuf sops;
    sops.sem_num = index;
    sops.sem_op = 1;
    sops.sem_flg = 0;
    if(semop(semid, &sops, 1) == -1){
        perror("semop");
        exit(EXIT_FAILURE);
    }
}


void * producer_work(void *){
    for(int i = 0; i < PRODUCT_COUNT; i++){
        //扣除一个生产者资源
        sem_wait(semid, 0);
        int item = rand()%100;
        buffer[in] = item;
        in = (in+1)%BUFF_SIZE;
        printf("生产者生产物品[%d]\n", item);
        sem_post(semid, 1);
        sleep(rand()%2+1);
    }
}

void * consumer_work(void *){
    for(int i = 0; i < PRODUCT_COUNT; i++){
        //扣除消费着资源
        sem_wait(semid, 1);
        int item = buffer[out];
        out = (out+1)%BUFF_SIZE;
        printf("消费者消费物品[%d]\n", item);
        sem_post(semid, 0);
        sleep(rand()%2+1);
    }
}

int main(){
    srand(time(NULL));
    key_t key = ftok(".", 'A');
    if(key == -1){
        perror("ftok");
        exit(EXIT_FAILURE);
    }

    semid = semget(key, 2, IPC_CREAT | 0666 | IPC_EXCL);
    if (semid == -1) {
        if (errno == EEXIST) {
            // 如果信号量集合已经存在，则打开它
            semid = semget(key, 2, 0666);
            if (semid == -1) {
                perror("semget");
                exit(EXIT_FAILURE);
            }
        } else {
            perror("semget");
            exit(EXIT_FAILURE);
        }
    } else {
        unsigned short num[2] ={BUFF_SIZE, 0};
        //初始化信号量集合
        if (semctl(semid, 0, SETALL, num) == -1) {
            perror("semctl");
            exit(EXIT_FAILURE);
        }
    }
    printf("create or open semaphore success!\n");

    // 生产者线程
    pthread_t producer_t;
    pthread_create(&producer_t, NULL, producer_work, NULL);

    //消费者线程
    pthread_t consumer_t;
    pthread_create(&consumer_t, NULL, consumer_work, NULL);

    pthread_join(producer_t, NULL);
    pthread_join(consumer_t, NULL);

    //删除信号量集合
    semctl(semid, 0, IPC_RMID);

    return 0;
}